package com.quectel.emp.controller.videopatrol;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.quectel.business.common.device.DeviceExtraAttrCls;
import com.quectel.business.dms.device.DeviceBusiness;
import com.quectel.constant.core.videopatrol.VideoPatrolEventReportConstants;
import com.quectel.constant.core.videopatrol.VideoPatrolTaskConstants;
import com.quectel.constant.core.workorder.WorkOrderConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.constant.global.annotation.RequirePermissions;
import com.quectel.core.module.dept.dto.DeptPersonDto;
import com.quectel.core.module.dept.service.DeptPersonService;
import com.quectel.core.module.device.dto.DeviceDto;
import com.quectel.core.module.device.service.DeviceService;
import com.quectel.core.module.patrol.dto.VideoPatrolEventReportDto;
import com.quectel.core.module.patrol.dto.VideoPatrolTaskDto;
import com.quectel.core.module.patrol.dto.VideoPatrolTaskSummaryDto;
import com.quectel.core.module.patrol.service.VideoPatrolEventReportService;
import com.quectel.core.module.patrol.service.VideoPatrolTaskService;
import com.quectel.core.module.patrol.service.VideoPatrolTaskSummaryService;
import com.quectel.core.module.securitypatrol.dto.SecurityPatrolTaskDto;
import com.quectel.core.module.workorder.dto.WorkOrderDto;
import com.quectel.emp.constants.ResourceConstants;
import com.quectel.emp.controller.BaseController;
import com.quectel.emp.interceptor.LoginEmpSession;
import com.quectel.emp.service.workorder.ApiWorkOrderService;
import com.quectel.emp.util.SessionHolder;
import com.quectel.emp.vo.videopatrol.SignInfoVo;
import com.quectel.emp.vo.videopatrol.VideoPatrolEventReportVo;
import com.quectel.emp.vo.videopatrol.req.SignInReq;
import com.quectel.emp.vo.videopatrol.resp.VideoPatrolTaskResp;
import com.quectel.util.common.*;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.redis.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

import static cn.hutool.core.text.CharSequenceUtil.splitTrim;

/**
 * @author bob
 * @email bob.yu@quectel.com
 * @date 2022/5/24 10:58
 */
@RestController
@RequestMapping("videoPatrol")
@Tag(name = "VideoPatrolController", description = "视频巡检相关的api")
public class VideoPatrolController extends BaseController {

    @DubboReference
    private VideoPatrolTaskService videoPatrolTaskService;
    @DubboReference
    private VideoPatrolEventReportService videoPatrolEventReportService;
    @DubboReference
    private VideoPatrolTaskSummaryService videoPatrolTaskSummaryService;
    @DubboReference
    private DeviceService deviceService;
    @DubboReference
    private DeptPersonService deptPersonService;

    @Autowired
    ApiWorkOrderService apiWorkOrderService;
    @Autowired
    private DeviceBusiness deviceBusiness;

    /**
     * 设备上报间隔
     */
    private static final int VIDEO_PATROL_REPORT_TIME_LIMIT = 3;
    private static final String EXEC_START_TIME = "execStartTime";
    private static final String EXEC_END_TIME = "execEndTime";
    private static final String EXEC_TIME = "execTime";
    private static final String STATUSES = "statuses";

    private static final String VIDEO_PATROL_REPORT_LIMIT_LOCK_BY_TASKID = "VIDEO_PATROL_REPORT_LIMIT_LOCK_BY_TASKID:";

    /**
     * @return
     */
    @PostMapping("signIn")
    @Operation(summary = "视频巡更,点位签到接口")
    @RequirePermissions(values = {ResourceConstants.VIDEO_PATROL})
    public Response<Object> signIn(@RequestBody SignInReq signInReq) {

        DeptPersonDto deptPerson = SessionHolder.getEmp();

        VideoPatrolTaskDto videoPatrolTaskDto = videoPatrolTaskService.selectById(signInReq.getTaskId());
        if (videoPatrolTaskDto == null
                || videoPatrolTaskDto.getExecStatus().equals(VideoPatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTED.getStatus())
                || videoPatrolTaskDto.getExecStatus().equals(VideoPatrolTaskConstants.TasksStatusEnum.STATUS_MISSED_DETECTION.getStatus())
        ) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }

        //校验是否在执行时间
        Date now = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(now);
        String time = new SimpleDateFormat("HH:mm").format(now);
        if (!Objects.equals(dateStr, videoPatrolTaskDto.getDate()) || time.compareTo(videoPatrolTaskDto.getPeriod().split("-")[0]) < 0 || time.compareTo(videoPatrolTaskDto.getPeriod().split("-")[1]) > 0) {
            return Response.error("当前时间不在该任务的时间段范围之内");
        }

        if (!videoPatrolTaskDto.getExecUserId().equals(deptPerson.getId())) {
            return Response.error("该员工不可执行该任务签到");
        }

        DeviceDto device = deviceService.selectByIdWithoutPadding(signInReq.getDeviceId());
        if (device == null) {
            return Response.error(SystemConstants.ILLEGAL_MSG);
        }


        List<SignInfoVo> signInfoVoList = new ArrayList<>();

        VideoPatrolTaskSummaryDto videoPatrolTaskSummaryDto = videoPatrolTaskSummaryService.queryByTaskId(signInReq.getTaskId());

        if (videoPatrolTaskSummaryDto == null) {
            // 巡更点位签到状态
            List<DeviceDto> deviceDtoList = videoPatrolTaskDto.getDeviceDtoList();
            for (DeviceDto deviceRecord : deviceDtoList) {
                SignInfoVo signInfo = new SignInfoVo();
                signInfo.setId(deviceRecord.getId());
                signInfo.setName(deviceRecord.getName());
                if (deviceRecord.getId().equals(signInReq.getDeviceId())) {
                    signInfo.setSignTime(new Date());
                }
                signInfoVoList.add(signInfo);
            }

            // 新增上报
            VideoPatrolTaskSummaryDto videoPatrolTaskSummary = new VideoPatrolTaskSummaryDto();
            videoPatrolTaskSummary.setTenantId(videoPatrolTaskDto.getTenantId());
            videoPatrolTaskSummary.setVillageId(videoPatrolTaskDto.getVillageId());
            videoPatrolTaskSummary.setTaskId(signInReq.getTaskId());
            videoPatrolTaskSummary.setTaskJson(JacksonUtils.toJsonString(videoPatrolTaskDto));
            videoPatrolTaskSummary.setTaskName(videoPatrolTaskDto.getName());
            videoPatrolTaskSummary.setSignDetail(JacksonUtils.toJsonString(signInfoVoList));
            videoPatrolTaskSummary.setExecUserId(deptPerson.getId());
            videoPatrolTaskSummary.setExecUsername(deptPerson.getName());
            videoPatrolTaskSummary.setCreateTime(new Date());
            videoPatrolTaskSummaryService.save(videoPatrolTaskSummary);
        } else {

            // 追加点位签到信息
            signInfoVoList = JacksonUtils.parseArray(videoPatrolTaskSummaryDto.getSignDetail(), SignInfoVo.class);
            for (SignInfoVo signInfoVo : signInfoVoList) {
                // 点位id和 签到时间不为空 再更新签到时间,不允许重复签到
                if (signInfoVo.getId().equals(signInReq.getDeviceId())
                        && signInfoVo.getSignTime() == null) {
                    signInfoVo.setSignTime(new Date());
                }
            }

            // 更新记录
            VideoPatrolTaskSummaryDto updately = new VideoPatrolTaskSummaryDto();
            updately.setId(videoPatrolTaskSummaryDto.getId());
            updately.setSignDetail(JacksonUtils.toJsonString(signInfoVoList));
            videoPatrolTaskSummaryService.updateById(updately);
        }

        /**
         * 判断点位是否都执行了，都执行就更新任务为完成 2023-4-14 17:29:09 确认
         */
        VideoPatrolTaskSummaryDto videoPatrolTaskSummary = videoPatrolTaskSummaryService.queryByTaskId(signInReq.getTaskId());
        List<SignInfoVo> signInfoVos = JacksonUtils.parseArray(videoPatrolTaskSummary.getSignDetail(), SignInfoVo.class);
        for (SignInfoVo info : signInfoVos) {
            if (info.getSignTime() == null) {
                return Response.ok();
            }
        }

        // 所有时间都不为空，更新任务状态为完成
        VideoPatrolTaskDto updately = new VideoPatrolTaskDto();
        updately.setId(videoPatrolTaskDto.getId());
        updately.setExecStatus(VideoPatrolTaskConstants.TasksStatusEnum.STATUS_EXECUTED.getStatus());
        updately.setEditTime(new Date());
        videoPatrolTaskService.updateById(updately);

        return Response.ok();
    }

    @GetMapping("list")
    @Operation(summary = "根据参数查询视频巡检任务分页列表")
    @RequirePermissions(values = {ResourceConstants.VIDEO_PATROL})
    public Response<Page<VideoPatrolTaskDto>> list(@Parameter(description = "巡检任务状态 0未开始 1进行中 2已结束") @RequestParam(required = false) String statusFlag,
                                                   @Parameter(description = "从1开始的页码数") @RequestParam Integer pageNo,
                                                   @Parameter(description = "每页多少条") @RequestParam Integer pageSize) {
        LoginEmpSession loginEmpSession = SessionHolder.getLoginEmpSession();
        Request request = Request.configParams(Request.parseParams(httpServletRequest));
        if (StringUtils.isNotBlank(statusFlag)) {
            request.getParams().put(STATUSES, splitTrim(statusFlag, ","));
        }

        Long deptPersonId = loginEmpSession.getDeptPersonDto().getId();
        request.put(LambdaUtil.getFieldName(SecurityPatrolTaskDto::getExecUserId), deptPersonId);

        List<VideoPatrolTaskDto> result = null;
        int total = 0;
        // 明天的和今天的当前时间之后的
        // 当前时间  <  任务开始时间  13:58计划开始  现在 13:51  那就是未开始
        if (statusFlag.contains("0")) {
            request.getParams().put(EXEC_START_TIME, EXEC_START_TIME);
            request.getParams().put("execStatus", 0); // 任务肯定是未执行的
        }

        // 当前时间 在 任务开始结束之间
        // 任务状态是未执行 或者 执行中
        if (statusFlag.contains("1")) {
            request.getParams().put(EXEC_TIME, EXEC_TIME);
            request.getParams().remove(STATUSES);
            request.put(STATUSES, splitTrim("0,1", ","));
        }

        // 当前时间 大于 任务结束时间  都是结束
        // 不限制任务状态。时间超了都是超
        if (statusFlag.contains("2")) {
            request.put(EXEC_END_TIME, EXEC_END_TIME);
            request.getParams().remove(STATUSES);
        }
        total = videoPatrolTaskService.queryTotal(request.getParams());
        if (total > 0) {
            result = videoPatrolTaskService.queryList(request.getParams());
            for (VideoPatrolTaskDto videoPatrolTaskDto : result) {
                videoPatrolTaskDto.setPlanJson(null);
                videoPatrolTaskDto.setVillageDto(null);
            }
        } else {
            result = new ArrayList<>();
        }

        Page<VideoPatrolTaskDto> page = new Page<>(request.getPageNo(), request.getPageSize(), total, result);
        return Response.<Page<VideoPatrolTaskDto>>ok().wrap(page);
    }

    /**
     * 查看视频巡检任务详情
     *
     * @param taskId
     * @return
     */
    @GetMapping("queryDetailByTaskId")
    @Operation(summary = "查看视频巡检任务详情")
    @RequirePermissions(values = {ResourceConstants.VIDEO_PATROL})
    public Response<VideoPatrolTaskResp> queryByTaskId(@Parameter(description = "巡检任务id") @RequestParam Long taskId) {
        SessionHolder.checkEmp();
        VideoPatrolTaskDto videoPatrolTaskDto = videoPatrolTaskService.selectById(taskId);

        if (videoPatrolTaskDto != null) {
            videoPatrolTaskDto.setPlanJson(null);
            if (videoPatrolTaskDto != null && CollectionUtil.isNotEmpty(videoPatrolTaskDto.getDeviceDtoList())) {
                List<String> streams = new ArrayList<>();
                for (DeviceDto deviceDto : videoPatrolTaskDto.getDeviceDtoList()) {
                    DeviceExtraAttrCls.AttrOfIntelligentMonitor attr = DeviceExtraAttrCls.parseExtStr(DeviceExtraAttrCls.AttrOfIntelligentMonitor.class
                            , deviceDto.getExtraAttr());

                    streams.add(attr.getStreamUrl());
                }
                videoPatrolTaskDto.setStreamUrls(streams);
            }
        }
        VideoPatrolTaskResp videoPatrolTaskResp = CopyUtils.copyObj(videoPatrolTaskDto, VideoPatrolTaskResp.class);

        // 查看巡查报告，没有填充空的，先填充巡更签到列表，在点位上报的时候再创建巡查报告记录
        VideoPatrolTaskSummaryDto videoPatrolTaskSummaryDto = videoPatrolTaskSummaryService.queryByTaskId(taskId);
        if (videoPatrolTaskSummaryDto != null) {
            videoPatrolTaskResp.setSignDetail(JacksonUtils.parseArray(videoPatrolTaskSummaryDto.getSignDetail(), SignInfoVo.class));
        } else {
            List<SignInfoVo> signInfoVoList = new ArrayList<>();
            // 巡更点位签到状态
            List<DeviceDto> deviceDtoList = videoPatrolTaskDto.getDeviceDtoList();
            for (DeviceDto device : deviceDtoList) {
                SignInfoVo signInfo = new SignInfoVo();
                signInfo.setId(device.getId());
                signInfo.setName(device.getName());
                signInfoVoList.add(signInfo);
            }
            videoPatrolTaskResp.setSignDetail(signInfoVoList);
        }

        // 工单信息填充在任务上
        Map<String, Object> reportSqlMap = new HashMap<>(3);
        reportSqlMap.put(LambdaUtil.getFieldName(VideoPatrolEventReportDto::getTenantId), videoPatrolTaskDto.getTenantId());
        reportSqlMap.put(LambdaUtil.getFieldName(VideoPatrolEventReportDto::getVillageId), videoPatrolTaskDto.getVillageId());
        reportSqlMap.put(LambdaUtil.getFieldName(VideoPatrolEventReportDto::getTaskId), videoPatrolTaskDto.getId());

        // 这里租户id 小区id taskId 联合唯一
        List<VideoPatrolEventReportDto> videoPatrolEventReportDtos = videoPatrolEventReportService.queryList(reportSqlMap);
        if (CollectionUtils.isNotEmpty(videoPatrolEventReportDtos)) {
            VideoPatrolEventReportDto videoPatrolEventReportDto = videoPatrolEventReportDtos.get(0);
            videoPatrolTaskResp.setReportTime(videoPatrolEventReportDto.getCreateTime());
            videoPatrolTaskResp.setWorkOrderNo(videoPatrolEventReportDto.getWorkOrderNo());
            videoPatrolTaskResp.setAssignUsername(videoPatrolEventReportDto.getAssignUsername());
            videoPatrolTaskResp.setSnapshotImg(videoPatrolEventReportDto.getSnapshotImg());
            videoPatrolTaskResp.setDetail(videoPatrolEventReportDto.getDetail());
        }

        return Response.<VideoPatrolTaskResp>ok().wrap(videoPatrolTaskResp);
    }


    /**
     * 上报视频巡检记录
     *
     * @param reportVo
     * @return
     */
    @PostMapping("report")
    @Operation(summary = "上报视频巡检记录")
    @RequirePermissions(values = {ResourceConstants.VIDEO_PATROL})
    public Response<Object> report(@RequestBody VideoPatrolEventReportVo reportVo) {
        LoginEmpSession loginEmpSession = SessionHolder.getLoginEmpSession();
        assert loginEmpSession != null;

        DeptPersonDto deptPersonDto = loginEmpSession.getDeptPersonDto();
        VideoPatrolTaskDto videoPatrolTaskDto = videoPatrolTaskService.selectById(reportVo.getTaskId());
        //参数校验
        if (videoPatrolTaskDto == null) {
            return Response.error("当前任务不存在");
        }

        if (reportVo.getSnapshotImg() != null) {
            JacksonUtils.parseArray(reportVo.getSnapshotImg(), String.class);
        }

        if (!RedisUtils.setnx(VIDEO_PATROL_REPORT_LIMIT_LOCK_BY_TASKID + videoPatrolTaskDto.getId() + SystemConstants.COLON + videoPatrolTaskDto.getExecUserId(),
                SystemConstants.YES.toString(), VIDEO_PATROL_REPORT_TIME_LIMIT, TimeUnit.SECONDS)) {
            throw new CheckErrorException("上报过于频繁");
        }

        if (!videoPatrolTaskDto.getExecUserId().equals(loginEmpSession.getDeptPersonDto().getId())) {
            return Response.error("员工不可执行该任务");
        }

        // 根据不同的任务类型，填充工单类型
        WorkOrderConstants.CategoryAndType workOrderCategoryAndType = null;

        workOrderCategoryAndType = WorkOrderConstants.CategoryAndType.VIDEO_PATROL;
        //校验是否在执行时间
        Date now = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd").format(now);
        String time = new SimpleDateFormat("HH:mm").format(now);
        if (!Objects.equals(dateStr, videoPatrolTaskDto.getDate()) || time.compareTo(videoPatrolTaskDto.getPeriod().split("-")[0]) < 0 || time.compareTo(videoPatrolTaskDto.getPeriod().split("-")[1]) > 0) {
            return Response.error("当前时间不在该任务的时间段范围之内");
        }
        VideoPatrolEventReportDto eventReportDto = new VideoPatrolEventReportDto();
        eventReportDto.setTenantId(videoPatrolTaskDto.getTenantId());
        eventReportDto.setVillageId(videoPatrolTaskDto.getVillageId());
        eventReportDto.setTaskId(videoPatrolTaskDto.getId());
        eventReportDto.setTaskName(videoPatrolTaskDto.getName());
        eventReportDto.setReportUserId(deptPersonDto.getId());
        eventReportDto.setReportUsername(deptPersonDto.getName());
        eventReportDto.setCreator(deptPersonDto.getName());
        eventReportDto.setCreateTime(new Date());
        eventReportDto.setAssignWorkOrder(VideoPatrolEventReportConstants.AssignWorkOrderStatusEnum.NO_ASSIGN_WORK_ORDER.getStatus());
        eventReportDto.setSnapshotImg(reportVo.getSnapshotImg());
        eventReportDto.setDetail(reportVo.getDetail());

        // 上报信息中的工单信息非空 数据填充
        if (VideoPatrolEventReportConstants.AssignWorkOrderStatusEnum.ASSIGN_WORK_ORDER.getStatus().equals(reportVo.getAssignWorkOrder())) {
            WorkOrderDto workOrderDto = apiWorkOrderService.patrolReportOrder(
                    loginEmpSession.getDeptPersonDto().getId(),
                    workOrderCategoryAndType,
                    reportVo.getDetail(),
                    reportVo.getSnapshotImg(),
                    reportVo.getAssignUserId(),
                    null
            );
            DeptPersonDto workOrderHander = deptPersonService.selectById(reportVo.getAssignUserId());
            eventReportDto.setAssignUserId(reportVo.getAssignUserId());
            eventReportDto.setAssignUsername(workOrderHander.getName());
            eventReportDto.setWorkOrderId(workOrderDto.getId());
            eventReportDto.setWorkOrderNo(workOrderDto.getOrderCode());
        }

        // 处理拼装的数据
        Lock spinLock = RedisUtils.getSpinLock(VIDEO_PATROL_REPORT_LIMIT_LOCK_BY_TASKID + videoPatrolTaskDto.getId());
        spinLock.lock();
        try {
            videoPatrolEventReportService.report(eventReportDto);
        } catch (Exception e) {
            throw new CheckErrorException("上报过于频繁");
        } finally {
            spinLock.unlock();
        }
        return Response.ok().wrap("上报完成");
    }

}
